#include <linux/netlink.h>
#include <net/netlink.h>
#include <linux/socket.h>
#include <net/sock.h>
#include <linux/input.h>
#include <linux/ktime.h>

#include "hdmi_cec.h"
#include "../dev_hdmi.h"

/* socket for hdme cec,comminucate with android */
struct sock* hdmi_cec_sock = NULL;
static struct task_struct * CEC_task;
static long unsigned long last_irq_time = 0;

static struct input_dev * hdmicecdev;
static volatile int receive_a_frame = 0;
static int hdmi_cec_polling_complete = 0;

typedef struct {
	__u32 cec_key;
	__u32 input_key;
} __disp_cec_key_tab;
static __disp_cec_key_tab cec_key_tab[] = {
{0x00, KEY_SELECT},
{0x01, KEY_UP},
{0x02, KEY_DOWN},
{0x03, KEY_LEFT},
{0x04, KEY_RIGHT},
{0x0D, KEY_BACK},
{0x2B, KEY_ENTER},
{0x40, KEY_POWER},
{0x41, KEY_VOLUMEUP},
{0x42, KEY_VOLUMEDOWN},
{0x43, KEY_MUTE}
};
static unsigned int last_button;

//physical addres
__u32 hdmi_cec_phy_addr;
int u_pid;
//gpio
__u32 hdmi_cec_gpio;
__u16 flag = 0;
extern __bool hdmi_cec_used;
//extern int hdmi_cec_pin;
//extern int hdmi_cec_pin_num;
extern __bool b_hdmi_suspend;
extern __bool hdmi_cec_wakeup_status;

extern hdmi_info_t ghdmi;
//polling message
static struct hrtimer hdmi_cec_polling_message_hrtimer;
ktime_t hdmi_cec_polling_message_kt;

__hdmi_cec_polling_message_data polling_mes_data;

//send frame
static struct hrtimer hdmi_cec_send_frame_hrtimer;
ktime_t hdmi_cec_send_frame_kt;
int irq_num;

__hdmi_cec_send_frame_data send_frame_data;
__hdmi_cec_send_message send_frame_buffer[10];

//receive frame
static struct hrtimer hdmi_cec_receive_frame_hrtimer;
ktime_t hdmi_cec_receive_frame_kt;

__hdmi_cec_receive_frame_data receive_frame_data;
__hdmi_cec_receive_message receive_frame_buffer[10];

__u32 hdmi_cec_get_pin()
{
    return ghdmi.hdmi_cec_cfg.hdmi_cec_pin;
}

__u32 hdmi_cec_get_pin_num()
{
    return ghdmi.hdmi_cec_cfg.hdmi_cec_pin_num;
}

__u32 hdmi_cec_set_gpio(__u32 pin,__u32 num)
{
    //pin: A B C D E F G... = 0 1 2 3 4 5 6...
    //num: 0~31
    int ret = -1;
    if (pin < 0 || pin > 6){
		__Cerr("##pin err.\n");
		return PIN_FAIL;
    }
	if(num < 0 || num > 31){
		__Cerr("##pin num err.\n");
		return PIN_NUM_ERR;
	}
    switch(pin){
        case 0:
			ret = GPIOA(num);
		    break;
		case 1:
			ret = GPIOB(num);
		    break;

		case 2:
			ret = GPIOC(num);
		    break;

		case 3:
			ret = GPIOD(num);
		    break;

		case 4:
			ret = GPIOE(num);
		    break;

		case 5:
			ret = GPIOF(num);
		    break;

		case 6:
			ret = GPIOG(num);
		    break;

		default:
			ret = GPIO_ERR;
    }
	return ret;
}

__u32 hdmi_cec_read_reg_bit(void)
{
	return gpio_get_value(hdmi_cec_gpio);
}

void hdmi_cec_write_reg_bit1(void)
{
	gpio_set_value(hdmi_cec_gpio, 1);
}

void hdmi_cec_write_reg_bit0(void)
{
	gpio_set_value(hdmi_cec_gpio, 0);
}

void hdmi_cec_send_info_logical_byte(__u32 data)
{
    __u32 offset = send_frame_data.flags /2 - 1;
    if(send_frame_data.flags %2 == 1) {
        hdmi_cec_write_reg_bit0();

        if((data >> offset) & 0x1)
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_LOGICAL1_LOW_TIME);
        else
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_LOGICAL0_LOW_TIME);
        } else {
            hdmi_cec_write_reg_bit1();

            if((data >> offset) & 0x1)
                hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_WHOLE_TIME - HDMI_CEC_DATA_BIT_LOGICAL1_LOW_TIME);
            else
                hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_WHOLE_TIME - HDMI_CEC_DATA_BIT_LOGICAL0_LOW_TIME);
            }

    send_frame_data.flags--;
}

void hdmi_cec_send_eom_logical_bit(void)
{
	if(send_frame_data.flags % 2 == 1) {
        hdmi_cec_write_reg_bit0();

        if(send_frame_data.eom == HDMI_CEC_MSG_EOM_MORE)
                hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_LOGICAL0_LOW_TIME);
        else
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_LOGICAL1_LOW_TIME);
        } else {
            hdmi_cec_write_reg_bit1();

            if(send_frame_data.eom == HDMI_CEC_MSG_EOM_MORE)
                hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_WHOLE_TIME - HDMI_CEC_DATA_BIT_LOGICAL0_LOW_TIME);
            else
                hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_WHOLE_TIME - HDMI_CEC_DATA_BIT_LOGICAL1_LOW_TIME);
            }

    send_frame_data.flags--;
}

void hdmi_cec_send_ack_logical_bit(void)
{
	if(send_frame_data.flags% 2 == 1) {
        hdmi_cec_write_reg_bit0();
        hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_LOGICAL1_LOW_TIME);
        } else {
            hdmi_cec_write_reg_bit1();
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_ACK_INVALID_TIME);
            }
    send_frame_data.flags--;
}

static enum hrtimer_restart hdmi_cec_polling_hrtimer_handler(struct hrtimer *timer)
{
	switch(polling_mes_data.sta) {
        case pre_polling_mes:
            if(hdmi_cec_read_reg_bit()) {
                polling_mes_data.status_flags--;

                if(polling_mes_data.status_flags == 0) {
                    sw_gpio_setcfg(hdmi_cec_gpio, 1);
                    hdmi_cec_write_reg_bit0();
                    polling_mes_data.sta = start_polling_bit;
                    hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_START_BIT_LOW_TIME);
                    }
                } else {
                    polling_mes_data.status_flags = 30;
                    }
            break;

        case start_polling_bit:
            hdmi_cec_write_reg_bit1();
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_START_BIT_HIGH_TIME);
            polling_mes_data.sta = polling_mes_set_info_bit;
            send_frame_data.flags = 17;
            break;

        case polling_mes_set_info_bit:
            if(send_frame_data.flags > 1)
                hdmi_cec_send_info_logical_byte(polling_mes_data.header_block);

            if(send_frame_data.flags == 1) {
                polling_mes_data.sta = polling_mes_set_eom_bit;
                send_frame_data.flags = 3;
                send_frame_data.eom = HDMI_CEC_MSG_EOM_END;
                }
            break;

        case polling_mes_set_eom_bit:
            if(send_frame_data.flags > 1)
                hdmi_cec_send_eom_logical_bit();

            if(send_frame_data.flags == 1) {
                polling_mes_data.sta = polling_mes_set_ack_bit;
                send_frame_data.flags = 3;
                }
            break;

        case polling_mes_set_ack_bit:
            if(send_frame_data.flags > 1)
                hdmi_cec_send_ack_logical_bit();

            if(send_frame_data.flags == 1) {
                polling_mes_data.sta = polling_mes_get_ack_bit;
                }

            if(polling_mes_data.broadcast_flags == 1) {
                polling_mes_data.broadcast_complete_flags = 1;
                printk("****************polling_mes_data broadcast_complete_flags\n");
                goto out_complete;
                }
            break;

        case polling_mes_get_ack_bit:
            sw_gpio_setcfg(hdmi_cec_gpio, 0);
            if(hdmi_cec_read_reg_bit()) {
                send_frame_data.indicate_logical_adr = polling_mes_data.header_block & 0xf;
                polling_mes_data.broadcast_flags = 1;
                polling_mes_data.sta = complete_polling_mes;
                } else {
                        polling_mes_data.sta = continue_polling_mes;
                        hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_CONFIRM_LOGICAL_BIT_TIME);

                        send_frame_data.resend_flags--;
                    }

            sw_gpio_setcfg(hdmi_cec_gpio, 1);

            break;

        case continue_polling_mes:
            if(send_frame_data.resend_flags == 0) {
                if((polling_mes_data.header_block & 0xf) == HDMI_CEC_LADDR_PLAYER1) {
                    polling_mes_data.header_block  = (HDMI_CEC_LADDR_PLAYER2 << 4) |HDMI_CEC_LADDR_PLAYER2;
                    send_frame_data.resend_flags = 5;
                    } else if((polling_mes_data.header_block & 0xf) == HDMI_CEC_LADDR_PLAYER2) {
                        polling_mes_data.header_block  = (HDMI_CEC_LADDR_PLAYER3 << 4) | HDMI_CEC_LADDR_PLAYER3;
                        send_frame_data.resend_flags = 5;
                        } else {
                            goto out_failed;
                            }
                }

            polling_mes_data.sta = pre_polling_mes;
            polling_mes_data.status_flags = 30;
            sw_gpio_setcfg(hdmi_cec_gpio, 0);
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_PRE_POLLING_MESSAGE_TIME);

            break;

        case complete_polling_mes:
            polling_mes_data.sta = pre_polling_mes;
            polling_mes_data.status_flags = 30;
            sw_gpio_setcfg(hdmi_cec_gpio, 0);
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_PRE_POLLING_MESSAGE_TIME);
            break;

        default :
            break;
        }

    return HRTIMER_RESTART;

    out_failed:
        receive_frame_data.sta = judge_start_bit;
        hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_JUDGE_START_BIT_TIME);
        sw_gpio_setcfg(hdmi_cec_gpio, 6);
        sw_gpio_eint_set_enable(hdmi_cec_gpio, 1);
        return HRTIMER_NORESTART;

    out_complete:
        receive_frame_data.sta = judge_start_bit;
        hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_JUDGE_START_BIT_TIME);
        sw_gpio_setcfg(hdmi_cec_gpio, 6);
        sw_gpio_eint_set_enable(hdmi_cec_gpio, 1);
		hdmi_cec_polling_complete = 1;

        return HRTIMER_NORESTART;
}

void hdmi_cec_polling_message(void)
{
    polling_mes_data.broadcast_complete_flags = 0;
    polling_mes_data.sta = pre_polling_mes;
    send_frame_data.resend_flags = 5;

    polling_mes_data.status_flags = 30;

    polling_mes_data.broadcast_flags = 0;

    sw_gpio_eint_set_enable(hdmi_cec_gpio, 0);

    sw_gpio_setcfg(hdmi_cec_gpio, 0);

    polling_mes_data.header_block = (HDMI_CEC_LADDR_PLAYER1 << 4) |HDMI_CEC_LADDR_PLAYER1;

    hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_PRE_POLLING_MESSAGE_TIME);
    hrtimer_start(&hdmi_cec_polling_message_hrtimer, hdmi_cec_send_frame_kt, HRTIMER_MODE_REL);
}

void hdmi_cec_clear_polling_flags(void)
{
    polling_mes_data.broadcast_complete_flags = 0;
}

int __disp_set_cec_active_source(void)
{
	unsigned int phy_addr;
	__hdmi_cec_send_message mes;

	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	phy_addr = hdmi_cec_phy_addr;
	mes.follower_adr = HDMI_CEC_LADDR_BROADCAST;
	mes.opcode = HDMI_CEC_ACTIVE_SOURCE;
	mes.oprands_num = 2;
	mes.oprands[0] = (phy_addr >> 8) & 0xff;
	mes.oprands[1] = phy_addr & 0xff;
	hdmi_send_message(&mes);
	
    return 0;
}

int __disp_set_cec_inactive_source(void)
{
    uint32_t phy_addr;
	__hdmi_cec_send_message mes;
	phy_addr = hdmi_cec_phy_addr;
	mes.oprands_num = 2;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_TV;
	mes.opcode = HDMI_CEC_INACTIVE_SOURCE;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	mes.oprands[0] = (phy_addr >> 8) & 0xff;
	mes.oprands[1] = phy_addr & 0xff;
	hdmi_send_message(&mes);

    return 0;
}

int __disp_set_cec_request_active_source(void)
{
	__hdmi_cec_send_message mes;
	mes.oprands_num = 0;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_BROADCAST;
	mes.opcode = HDMI_CEC_REQUEST_ACTIVE_SOURCE;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	mes.oprands[0] = 0;
	hdmi_send_message(&mes);
    return 0;
}

int __disp_set_cec_dev_physical_addr_to_tv(void)
{
	unsigned int phy_addr;
    __hdmi_cec_send_message mes;
	mes.oprands_num = 3;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_BROADCAST;
	mes.opcode = HDMI_CEC_REPORT_PHY_ADDR;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	phy_addr = hdmi_cec_phy_addr;
	mes.oprands[0] = (phy_addr >> 8) & 0xff;
	mes.oprands[1] = phy_addr & 0xff;
	mes.oprands[2] = 0x4;
	hdmi_send_message(&mes);

    return 0;
}

int __disp_set_cec_send_vendor_id(void)
{
	__hdmi_cec_send_message mes;

	mes.oprands_num = 2;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_BROADCAST;
	mes.opcode = HDMI_CEC_SENT_VERDOR_ID;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	mes.oprands[0] = (0x238 >> 8) & 0xff;
	mes.oprands[1] = 0x238 & 0xff;

/*
	mes.oprands_num = 7;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_BROADCAST;
	mes.opcode = HDMI_CEC_SENT_VERDOR_ID;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	mes.oprands[0] = 7;
	mes.oprands[1] = 0;
	mes.oprands[2] = 46;
	mes.oprands[3] = 0;
	mes.oprands[4] = 5;
	mes.oprands[5] = 0;
	mes.oprands[6] = 2;
*/
	hdmi_send_message(&mes);

    return 0;
}

int __disp_set_cec_power_status_to_tv(void)
{
	__hdmi_cec_send_message mes;
	mes.oprands_num = 1;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_TV;
	mes.opcode = HDMI_CEC_REPORT_POWER_STATUS;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	mes.oprands[0] = (b_hdmi_suspend == 0)?0:1;
	hdmi_send_message(&mes);

	return 0;
}

int __disp_set_cec_image_view_on(void)
{
	__hdmi_cec_send_message mes;
	mes.oprands_num = 0;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_TV;
	mes.opcode = HDMI_CEC_IMAGE_VIEW_ON;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	hdmi_send_message(&mes);

	return 0;
}

int __disp_set_cec_osd_name(void)
{
    int i,name_size;
    char *p_osd_n = HDMI_CEC_OSD_NAME;
	__hdmi_cec_send_message mes;

	name_size = strlen(p_osd_n);
	if (name_size > 14){
          __Cinf("####the size of name is too long,and get 14 letters.\n");
          name_size = 14;
    }
	mes.oprands_num = name_size;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_TV;
	mes.opcode = HDMI_CEC_SET_OSD_NAME;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	for (i = 0 ; i < name_size; i++)
         mes.oprands[i] = p_osd_n[i];//*(p_osd_n + i);
	hdmi_send_message(&mes);

	return 0;
}

int __disp_set_cec_text_view_on(void)
{
	__hdmi_cec_send_message mes;
	mes.oprands_num = 0;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_TV;
	mes.opcode = HDMI_CEC_TEXT_VIEW_ON;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	hdmi_send_message(&mes);

	return 0;
}

static void __disp_cec_receive_tv_standby(void)
{
	input_report_key(hdmicecdev, KEY_POWER, 1);
	input_sync(hdmicecdev);
 	msleep(100);
	input_report_key(hdmicecdev, KEY_POWER, 0);
	input_sync(hdmicecdev);
}

int __disp_set_cec_version_to_tv(void)
{
	__hdmi_cec_send_message mes;
	mes.oprands_num = 1;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_TV;
	mes.opcode = HDMI_CEC_REPORT_CEC_VERSION;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	mes.oprands[0] = 0x03; //Version 1.3
	hdmi_send_message(&mes);

	return 0;
}

int __disp_set_cec_menu_status_to_tv(void)
{
	__hdmi_cec_send_message mes;
	mes.oprands_num = 1;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_TV;
	mes.opcode = HDMI_CEC_MENU_STATE;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	/* Activated: 0, Deactivated:1 */
	mes.oprands[0] = (b_hdmi_suspend == 0)?0:1;;
	hdmi_send_message(&mes);

	return 0;
}

int __disp_get_menu_language(void)
{
	__hdmi_cec_send_message mes;
	mes.oprands_num = 0;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_TV;
	mes.opcode = HDMI_CEC_GET_MENU_LANGUAGE;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	hdmi_send_message(&mes);

	return 0;
}

int __disp_set_cec_send_routing_inf_to_tv(void)
{
	uint32_t phy_addr;
    __hdmi_cec_send_message mes;
	mes.oprands_num = 2;
	mes.opcode_flags = 1;
	mes.follower_adr = HDMI_CEC_LADDR_BROADCAST;
	mes.opcode = HDMI_CEC_SENT_ROUTING_INFORMATION;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	phy_addr = hdmi_cec_phy_addr;
	mes.oprands[0] = (phy_addr >> 8) & 0xff;
	mes.oprands[1] = phy_addr & 0xff;
	hdmi_send_message(&mes);

    return 0;
}

int __disp_set_cec_hdmi_ch(void)
{
    __hdmi_cec_send_message mes;
	mes.follower_adr = HDMI_CEC_LADDR_BROADCAST;
	mes.opcode = HDMI_CEC_ACTIVE_SOURCE;
	mes.oprands_num = 2;
	mes.opcode_flags = 1;
	memset(mes.oprands, 0, 14 * sizeof(unsigned long));
	mes.oprands[0] = (hdmi_cec_phy_addr >> 8) & 0xff;
	mes.oprands[1] = hdmi_cec_phy_addr & 0xff;
	hdmi_send_message(&mes);
    return 0;
}

int __disp_get_routing_change(__hdmi_cec_receive_message *process_data)
{
	if(process_data->oprands_num == 4)
	{
		unsigned int original_addr, new_addr;
		original_addr = ((process_data->oprands[0] & 0xff) << 8) | (process_data->oprands[1] & 0xff);
		new_addr = ((process_data->oprands[2] & 0xff) << 8) | (process_data->oprands[3] & 0xff);
		//printk("original phy addr = 0x%x, new phy addr = 0x%x", original_addr, new_addr);
		if(hdmi_cec_phy_addr == new_addr)
		{
			__disp_set_cec_send_routing_inf_to_tv();
		}
		else if (hdmi_cec_phy_addr == original_addr)
		{
			hdmi_cec_phy_addr = new_addr;
			__disp_set_cec_dev_physical_addr_to_tv();
			__disp_set_cec_active_source();
		}
	}

	return 0;
}

int __disp_set_cec_stream_path(__hdmi_cec_receive_message *process_data)
{
	unsigned int phy_addr;

	if(process_data->oprands_num == 2)
	{
		phy_addr = ((process_data->oprands[0] & 0xff) << 8) | (process_data->oprands[1] & 0xff);
		if(hdmi_cec_phy_addr != phy_addr)
		{
			hdmi_cec_phy_addr = phy_addr;
			//printk("cec stream path change, hdmi_cec_phy_addr = 0x%x\n", hdmi_cec_phy_addr);
		}
		if(b_hdmi_suspend == 1)
		{
			__disp_cec_receive_tv_standby();
			//printk("cec stream path change, set system standby 0\n");
		}
		__disp_set_cec_active_source();
	}

	return 0;
}

int __disp_cec_receive_user_control(__hdmi_cec_receive_message *process_data)
{
	if(process_data->opcode == 0x44)
	{
		int i;
		for(i=0; i < sizeof(cec_key_tab)/sizeof(__disp_cec_key_tab); i++)
		{
			if(cec_key_tab[i].cec_key == process_data->oprands[0])
			{
				//printk("key = %d press\n", cec_key_tab[i].input_key);
				input_report_key(hdmicecdev, cec_key_tab[i].input_key, 1);
				input_sync(hdmicecdev);
				last_button = cec_key_tab[i].input_key;
			}
		}
	}
	else
	{
		//printk("key: %d release\n", last_button);
		input_report_key(hdmicecdev, last_button, 0);
		input_sync(hdmicecdev);
	}

	return 0;
}

static void __disp_cec_data_process(__hdmi_cec_receive_message *process_data)
{
	if(hdmi_cec_polling_complete == 1)
	{
		//printk("hdmi_cec_polling_complete = 1, set serial operation\n");
		hdmi_cec_polling_complete = 0;
		mdelay(20);
		__disp_set_cec_dev_physical_addr_to_tv();
		mdelay(20);
		__disp_set_cec_image_view_on();
		mdelay(20);
		__disp_get_menu_language();
		mdelay(20);
		__disp_set_cec_active_source();
		mdelay(20);
		__disp_set_cec_menu_status_to_tv();
		mdelay(20);
		__disp_set_cec_osd_name();
		mdelay(20);
		__disp_set_cec_power_status_to_tv();
		mdelay(20);
	}
	switch(process_data->opcode)
	{
		/* Report OSD name */
		case 0x46:
			//mdelay(20);
			__disp_set_cec_osd_name();
			break;

		/* Get Routing Change */
		case 0x80:
			__disp_get_routing_change(process_data);
			break;

		/* Report Physical Address */
		case 0x83:
			//mdelay(20);
			__disp_set_cec_dev_physical_addr_to_tv();
			break;

		/* set active source */
		case 0x85:
			__disp_set_cec_active_source();
			break;
		
		/* Set stream path  */
		case 0x86:
			__disp_set_cec_stream_path(process_data);
			break;

		/* Report vendor ID */
		case 0x8c:
			//mdelay(20);
			__disp_set_cec_send_vendor_id();
			break;

		/* Report power status */
		case 0x8f:
			__disp_set_cec_power_status_to_tv();
			break;

		/* report CEC Version*/
		case 0x9f:
			__disp_set_cec_version_to_tv();
			break;

		case 0x8d:
			printk("get tv Menu Request\n");
			break;
	
		case 0x44:
		case 0x45:
			__disp_cec_receive_user_control(process_data);
			break;
	
		case 0x36:
			__disp_cec_receive_tv_standby();
			printk("cec receive tv standby\n");
			break;
		default:
			printk("unknown command: 0x%x, need deal with?\n", process_data->opcode);
	}
}

//hrtimer interrupt
static enum hrtimer_restart hdmi_cec_send_frame_hrtimer_handler(struct hrtimer *timer)
{
	switch(send_frame_data.sta) {
    case pre_send_mes:
        if(hdmi_cec_read_reg_bit()) {
        send_frame_data.status_flags--;

        if(send_frame_data.status_flags == 0) {
            sw_gpio_setcfg(hdmi_cec_gpio, 1);
            hdmi_cec_write_reg_bit0();
            send_frame_data.sta = start_bit;
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_START_BIT_LOW_TIME);
            }
        } else {
                send_frame_data.status_flags = 30;
                }
        break;

    case start_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_buffer[send_frame_data.current_buf_flags].follower_adr== HDMI_CEC_LADDR_BROADCAST)
            send_frame_data.ack = HDMI_CEC_MSG_ACK_BROADCAST;
        else
            send_frame_data.ack = HDMI_CEC_MSG_ACK_NORMAL;

        hdmi_cec_write_reg_bit1();
        hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_START_BIT_HIGH_TIME);
        send_frame_data.sta = header_block_set_info_bit;
        send_frame_data.flags = 17;

        send_frame_data.oprands_count = 0;
        break;

    case header_block_set_info_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_data.flags > 1)
            hdmi_cec_send_info_logical_byte((send_frame_data.indicate_logical_adr << 4) | send_frame_buffer[send_frame_data.current_buf_flags].follower_adr);

        if(send_frame_data.flags == 1) {
            send_frame_data.sta = header_block_set_eom_bit;
            send_frame_data.flags = 3;

            if(send_frame_buffer[send_frame_data.current_buf_flags].opcode_flags == 1){
                send_frame_data.eom = HDMI_CEC_MSG_EOM_MORE;
                send_frame_data.opcode_flags = 1;
                } else {
                    send_frame_data.eom = HDMI_CEC_MSG_EOM_END;
                    send_frame_data.opcode_flags = 0;
                    }
            }

        break;

    case header_block_set_eom_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_data.flags > 1)
            hdmi_cec_send_eom_logical_bit();

        if(send_frame_data.flags == 1) {
            send_frame_data.sta = header_block_set_ack_bit;
            send_frame_data.flags = 3;
            }

        break;

    case header_block_set_ack_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_data.flags > 1)
            hdmi_cec_send_ack_logical_bit();

        if(send_frame_data.flags == 1)
            send_frame_data.sta = header_block_get_ack_bit; 

        break;

    case header_block_get_ack_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        sw_gpio_setcfg(hdmi_cec_gpio, 0);

        if(((send_frame_data.ack == HDMI_CEC_MSG_ACK_NORMAL) && (hdmi_cec_read_reg_bit() == 1))
            ||((send_frame_data.ack == HDMI_CEC_MSG_ACK_BROADCAST) && (hdmi_cec_read_reg_bit() == 0)))
            {
                if(send_frame_data.resend_flags > 0)
                    {
                        send_frame_data.sta = continue_send_mes;
                        send_frame_data.resend_flags--;
                        break;
                    }
            }

        if(send_frame_data.eom == HDMI_CEC_MSG_EOM_MORE) {
            send_frame_data.sta = opcode_block_set_info_bit;
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_ACK_SET_LOW_TIME);
            send_frame_data.flags = 17;
            } else {
                send_frame_data.sta = continue_send_mes;
                send_frame_data.resend_flags = 5;

                send_frame_data.current_buf_flags++;
                if(send_frame_data.current_buf_flags > 9)
                    send_frame_data.current_buf_flags = 0;

                 if(send_frame_data.current_buf_flags == send_frame_data.add_buf_flags)
                    {
                        send_frame_data.current_buf_flags = 0;
                        send_frame_data.add_buf_flags = 0;
                        goto out_complete;
                    }
                }

        sw_gpio_setcfg(hdmi_cec_gpio, 1);
        break;

    case opcode_block_set_info_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_data.flags > 1)
            hdmi_cec_send_info_logical_byte(send_frame_buffer[send_frame_data.current_buf_flags].opcode);

        if(send_frame_data.flags == 1) {
            send_frame_data.sta = opcode_block_set_eom_bit;
            send_frame_data.flags = 3;

            if(send_frame_buffer[send_frame_data.current_buf_flags].oprands_num == 0) {
                send_frame_data.eom = HDMI_CEC_MSG_EOM_END;
                } else {
                    send_frame_data.eom = HDMI_CEC_MSG_EOM_MORE;
                    }
            }
        break;

    case opcode_block_set_eom_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_data.flags > 1)
            hdmi_cec_send_eom_logical_bit();

        if(send_frame_data.flags == 1) {
            send_frame_data.sta = opcode_block_set_ack_bit;
            send_frame_data.flags = 3;
            }

        break;

    case opcode_block_set_ack_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_data.flags > 1)
            hdmi_cec_send_ack_logical_bit();

        if(send_frame_data.flags == 1)
            send_frame_data.sta = opcode_block_get_ack_bit;

        break;

    case opcode_block_get_ack_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        sw_gpio_setcfg(hdmi_cec_gpio, 0);

        if(((send_frame_data.ack == HDMI_CEC_MSG_ACK_NORMAL) && (hdmi_cec_read_reg_bit() == 1))
            ||((send_frame_data.ack == HDMI_CEC_MSG_ACK_BROADCAST) && (hdmi_cec_read_reg_bit() == 0)))
            {
                if(send_frame_data.resend_flags > 0)
                    {
                        send_frame_data.sta = continue_send_mes;
                        send_frame_data.resend_flags--;
                        break;
                    }
            }

        sw_gpio_setcfg(hdmi_cec_gpio, 1);

        if(send_frame_data.eom == HDMI_CEC_MSG_EOM_END) {
            send_frame_data.sta = continue_send_mes;
            send_frame_data.resend_flags = 5;

            send_frame_data.current_buf_flags++;
            if(send_frame_data.current_buf_flags > 9)
                send_frame_data.current_buf_flags = 0;

             if(send_frame_data.current_buf_flags == send_frame_data.add_buf_flags) {
                    send_frame_data.current_buf_flags = 0;
                    send_frame_data.add_buf_flags = 0;
                    goto out_complete;
                }
            } else {
                send_frame_data.sta = oprands_block_set_info_bit;
                hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_ACK_SET_LOW_TIME);
                send_frame_data.flags = 17;
                }

        break;

    case oprands_block_set_info_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_data.flags > 1)
            hdmi_cec_send_info_logical_byte(send_frame_buffer[send_frame_data.current_buf_flags].oprands[send_frame_data.oprands_count]);

        if(send_frame_data.flags == 1) {
            send_frame_data.sta = oprands_block_set_eom_bit;
            send_frame_data.flags = 3;

            send_frame_data.oprands_count++;

            if(send_frame_data.oprands_count < send_frame_buffer[send_frame_data.current_buf_flags].oprands_num)
                send_frame_data.eom = HDMI_CEC_MSG_EOM_MORE;
            else
                send_frame_data.eom = HDMI_CEC_MSG_EOM_END;
            }

        break;

    case oprands_block_set_eom_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_data.flags > 1)
            hdmi_cec_send_eom_logical_bit();

        if(send_frame_data.flags == 1) {
            send_frame_data.sta = oprands_block_set_ack_bit;
            send_frame_data.flags = 3;
            }

        break;

    case oprands_block_set_ack_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        if(send_frame_data.flags > 1)
            hdmi_cec_send_ack_logical_bit();

        if(send_frame_data.flags == 1)
            send_frame_data.sta = oprands_block_get_ack_bit;

        break;

    case oprands_block_get_ack_bit:
        if(polling_mes_data.broadcast_complete_flags == 0) {
            send_frame_data.sta = wait_for_hdmi_connect;
             hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_WAIT_BROADCAST_COMPLETE);
             break;
            }

        sw_gpio_setcfg(hdmi_cec_gpio, 0);

        if(((send_frame_data.ack == HDMI_CEC_MSG_ACK_NORMAL) && (hdmi_cec_read_reg_bit() == 1))
            ||((send_frame_data.ack == HDMI_CEC_MSG_ACK_BROADCAST) && (hdmi_cec_read_reg_bit() == 0)))
            {
                if(send_frame_data.resend_flags > 0)
                    {
                        send_frame_data.sta = continue_send_mes;
                        send_frame_data.resend_flags--;
                        break;
                    }
            }

        sw_gpio_setcfg(hdmi_cec_gpio, 1);

        if(send_frame_data.eom == HDMI_CEC_MSG_EOM_MORE) {
            send_frame_data.sta = oprands_block_set_info_bit;
            send_frame_data.flags = 17;
            hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_DATA_BIT_ACK_SET_LOW_TIME);
            } else {
                send_frame_data.sta = continue_send_mes;
                send_frame_data.resend_flags = 5;

                send_frame_data.current_buf_flags++;
                if(send_frame_data.current_buf_flags > 9)
                    send_frame_data.current_buf_flags = 0;

                 if(send_frame_data.current_buf_flags == send_frame_data.add_buf_flags) {
                        send_frame_data.current_buf_flags = 0;
                        send_frame_data.add_buf_flags = 0;
                        goto out_complete;
                    }
                }

        break;

    case wait_for_hdmi_connect:
        if(polling_mes_data.broadcast_complete_flags == 1) {
            send_frame_data.sta = pre_send_mes;
            send_frame_data.oprands_count = 0;
            send_frame_data.status_flags = 30;

            send_frame_data.resend_flags = 5;

            sw_gpio_setcfg(hdmi_cec_gpio, 0);
            }
        break;

    case continue_send_mes:
        send_frame_data.sta = pre_send_mes;
        send_frame_data.oprands_count= 0;
        send_frame_data.status_flags = 30;
        hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_PRE_SEND_MESSAGE_TIME);
        send_frame_data.eom = HDMI_CEC_MSG_EOM_MORE;
        sw_gpio_setcfg(hdmi_cec_gpio, 0);

        break;

    default:
        break;
        }

    hrtimer_forward_now(timer, hdmi_cec_send_frame_kt);
    return HRTIMER_RESTART;

    out_complete:
    receive_frame_data.sta = judge_start_bit;
    hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_JUDGE_START_BIT_TIME);
    sw_gpio_setcfg(hdmi_cec_gpio, 6);
    sw_gpio_eint_set_enable(hdmi_cec_gpio, 1);
    return HRTIMER_NORESTART;
}

//start send frame
__u32 hdmi_cec_start_send_frame(void)
{
    if(polling_mes_data.broadcast_complete_flags == 1) {
        send_frame_data.sta = pre_send_mes;
        send_frame_data.oprands_count = 0;
        send_frame_data.oprands_num = send_frame_buffer[send_frame_data.current_buf_flags].oprands_num;

        send_frame_data.status_flags = 30;

        send_frame_data.resend_flags = 5;
        sw_gpio_eint_set_enable(hdmi_cec_gpio, 0);
        sw_gpio_setcfg(hdmi_cec_gpio, 0);

        send_frame_data.eom = HDMI_CEC_MSG_EOM_MORE;

        hdmi_cec_send_frame_kt = ktime_set(0, HDMI_CEC_PRE_SEND_MESSAGE_TIME);

        hrtimer_start(&hdmi_cec_send_frame_hrtimer, hdmi_cec_send_frame_kt, HRTIMER_MODE_REL);
        }
    return 0;
}

void hdmi_send_message(__hdmi_cec_send_message *mes)
{
    int i;
    if(hdmi_cec_used && polling_mes_data.broadcast_complete_flags == 1){

		#if 0
		__Cinf("###opcode = 0x%x,oprands_num = %d\n",mes->opcode, mes->oprands_num);
	    __Cinf("###follower_adr = %d,opcode_flag = %d\n",mes->follower_adr, mes->opcode_flags);
		__Cinf("###oprands[0] = %d,oprands[1] = %d\n",mes->oprands[0], mes->oprands[1]);
		#endif

        send_frame_buffer[send_frame_data.add_buf_flags].opcode_flags= mes->opcode_flags;
        send_frame_buffer[send_frame_data.add_buf_flags].follower_adr= (mes->follower_adr) & 0xf;
        send_frame_buffer[send_frame_data.add_buf_flags].oprands_num = mes->oprands_num;

        if(mes->opcode_flags) {
            send_frame_buffer[send_frame_data.add_buf_flags].opcode = (mes->opcode) & 0xff;
            for(i = 0; i < mes->oprands_num; i++)
                send_frame_buffer[send_frame_data.add_buf_flags].oprands[i] = (mes->oprands[i]) & 0xff;
            }

        if((send_frame_data.current_buf_flags == 0) && (send_frame_data.add_buf_flags == 0))
		{
			int i;
			for(i = 0; i < 20; i++)
			{
				if(receive_frame_data.sta < header_block_receive_info_bit)
				{
					hdmi_cec_start_send_frame();
					break;
				}
				else
				{
					//printk("CEC pin busy, wait 5ms\n");
					mdelay(5);
				}
			}
		}

        send_frame_data.add_buf_flags++;

        if(send_frame_data.add_buf_flags > 9)
            send_frame_data.add_buf_flags = 0;
        }
}

//receive frame function
void hdmi_cec_receive_info_logical_byte(void)
{
    receive_frame_data.receive_frame = (receive_frame_data.receive_frame |hdmi_cec_read_reg_bit());

    if(receive_frame_data.flags > 1)
        receive_frame_data.receive_frame = receive_frame_data.receive_frame << 1;

    receive_frame_data.flags--;
}

//receive frame high resolution timer handler
static enum hrtimer_restart hdmi_cec_receive_frame_hrtimer_handler(struct hrtimer *timer)
{
	switch(receive_frame_data.sta) {
    case judge_start_bit:
//        if(hdmi_cec_read_reg_bit() == 0) {
        if(1) {
/*            receive_frame_data.sta = confirm_start_bit;
            hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_CONFIRM_START_BIT_TIME);
            goto out_hrtimer_restart;*/
		} else {
			/* sent socket data here. */
			// __hdmi_cec_receive_message sec;
			//if(flag && hdmi_cec_receive_message(&sec))
			//     hdmi_cec_sent_sock_skb(&sec);

			/* if true,get data to send. false, no message to be send.*/
			if(receive_frame_data.current_buf_flags != receive_frame_data.add_buf_flags){
				;//hdmi_cec_run_thread will read the receive frame
			}

			//receive_frame_data.sta = judge_start_bit;
			//hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_JUDGE_START_BIT_TIME);
		   // receive_frame_data.receive_frame = 0x0;
		}
        break;

    case confirm_start_bit:
        if(hdmi_cec_read_reg_bit() == 1) {
            receive_frame_data.sta = header_block_receive_info_bit;
            hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_CONFIRM_LOGICAL_BIT_TIME);
            receive_frame_data.flags = 8;
            receive_frame_data.receive_frame = 0x0;

            receive_frame_data.oprands_count= 0;
            } else {
                receive_frame_data.sta = judge_start_bit;
                hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_JUDGE_START_BIT_TIME);
                receive_frame_data.receive_frame = 0x0;
                }

        break;

    case header_block_receive_info_bit:
        if(receive_frame_data.flags > 0)
            hdmi_cec_receive_info_logical_byte();

        if(receive_frame_data.flags == 0) {
            receive_frame_data.header_block = receive_frame_data.receive_frame;

            if(((receive_frame_data.header_block & 0xf) == send_frame_data.indicate_logical_adr) ||
                ((receive_frame_data.header_block & 0xf) == HDMI_CEC_LADDR_BROADCAST)) {
                receive_frame_data.sta = header_block_receive_eom_bit;
                receive_frame_data.receive_frame = 0x0;
                } else {
                    receive_frame_data.sta = judge_start_bit;
                    //hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_JUDGE_START_BIT_TIME);
                    }
            }
        break;

    case header_block_receive_eom_bit:
        if(hdmi_cec_read_reg_bit() == 0) {
            receive_frame_data.eom = HDMI_CEC_MSG_EOM_MORE;

            //add receive message list
            receive_frame_data.add_buf_flags++;

            if(receive_frame_data.add_buf_flags > 9)
                receive_frame_data.add_buf_flags = 0;

            if(receive_frame_data.add_buf_flags == receive_frame_data.current_buf_flags)
                receive_frame_data.current_buf_flags++;

            if(receive_frame_data.current_buf_flags > 9)
                receive_frame_data.current_buf_flags = 0;

            receive_frame_data.opcode_flags = 1;
            } else {
                receive_frame_data.eom = HDMI_CEC_MSG_EOM_END;
                }

        receive_frame_data.sta = header_block_ack_bit_pull_down;
        hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_PULL_LOW_TIME);

        break;

    case header_block_ack_bit_pull_down:
        if((receive_frame_data.header_block & 0xf) == send_frame_data.indicate_logical_adr) {
            sw_gpio_eint_set_enable(hdmi_cec_gpio, 0);
            sw_gpio_setcfg(hdmi_cec_gpio, 1);
            hdmi_cec_write_reg_bit0();
            }

        receive_frame_data.sta = header_block_ack_bit_pull_up;
        hdmi_cec_receive_frame_kt = ktime_set(0,  HDMI_CEC_DATA_BIT_LOGICAL0_LOW_TIME - HDMI_CEC_PULL_LOW_TIME);
        goto out_hrtimer_restart;

        break;

    case header_block_ack_bit_pull_up:
        if((receive_frame_data.header_block & 0xf) == send_frame_data.indicate_logical_adr) {
            hdmi_cec_write_reg_bit1();
            sw_gpio_setcfg(hdmi_cec_gpio, 6);
            sw_gpio_eint_set_enable(hdmi_cec_gpio, 1);
            }

        if(receive_frame_data.eom == HDMI_CEC_MSG_EOM_MORE) {
            receive_frame_data.sta = data_block_receive_info_bit;
            hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_CONFIRM_LOGICAL_BIT_TIME);
            receive_frame_data.flags = 8;
            receive_frame_data.oprands_count= 0;
            receive_frame_data.receive_frame = 0x0;
            } else {
                receive_frame_data.sta = judge_start_bit;
                //hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_JUDGE_START_BIT_TIME);
                }

        break;

    case data_block_receive_info_bit:
        if(receive_frame_data.flags > 0)
            hdmi_cec_receive_info_logical_byte();

        if(receive_frame_data.flags == 0) {
            if(receive_frame_data.opcode_flags == 1) {
                receive_frame_buffer[receive_frame_data.add_buf_flags].opcode = receive_frame_data.receive_frame;
                receive_frame_data.opcode_flags = 0;
				//printk("get opcode = %x\n", receive_frame_buffer[receive_frame_data.add_buf_flags].opcode);
                } else {
                    receive_frame_buffer[receive_frame_data.add_buf_flags].oprands[receive_frame_data.oprands_count] = receive_frame_data.receive_frame;
                    receive_frame_data.oprands_count++;
                    }

            receive_frame_data.sta = data_block_receive_eom_bit;
            receive_frame_data.receive_frame = 0x0;
            }

        break;

    case data_block_receive_eom_bit:
        if(hdmi_cec_read_reg_bit() == 0) {
            receive_frame_data.eom = HDMI_CEC_MSG_EOM_MORE;
            } else {
                receive_frame_data.eom = HDMI_CEC_MSG_EOM_END;

				receive_frame_buffer[receive_frame_data.add_buf_flags].oprands_num = receive_frame_data.oprands_count;
				receive_a_frame = 1;
			}

        receive_frame_data.sta = data_block_ack_bit_pull_down;
        hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_PULL_LOW_TIME);

        break;

    case data_block_ack_bit_pull_down:
        if((receive_frame_data.header_block & 0xf) == send_frame_data.indicate_logical_adr) {
            sw_gpio_eint_set_enable(hdmi_cec_gpio, 0);
            sw_gpio_setcfg(hdmi_cec_gpio, 1);
            hdmi_cec_write_reg_bit0();
            }

        receive_frame_data.sta = data_block_ack_bit_pull_up;
        hdmi_cec_receive_frame_kt = ktime_set(0,  HDMI_CEC_DATA_BIT_LOGICAL0_LOW_TIME - HDMI_CEC_PULL_LOW_TIME);
        goto out_hrtimer_restart;

        break;

    case data_block_ack_bit_pull_up:
        if((receive_frame_data.header_block & 0xf) == send_frame_data.indicate_logical_adr) {
            hdmi_cec_write_reg_bit1();
            sw_gpio_setcfg(hdmi_cec_gpio, 6);
            sw_gpio_eint_set_enable(hdmi_cec_gpio, 1);
            }

        if(receive_frame_data.eom == HDMI_CEC_MSG_EOM_MORE) {
            receive_frame_data.sta = data_block_receive_info_bit;
            hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_CONFIRM_LOGICAL_BIT_TIME);
            receive_frame_data.flags = 8;
            receive_frame_data.receive_frame = 0x0;
            } else {
                //receive_frame_buffer[receive_frame_data.add_buf_flags].oprands_num = receive_frame_data.oprands_count;
				//receive_a_frame = 1;
                receive_frame_data.sta = judge_start_bit;
                //hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_JUDGE_START_BIT_TIME);
			}
        break;

    default :
        break;
        }

    return HRTIMER_NORESTART;

    out_hrtimer_restart: hrtimer_forward_now(timer, hdmi_cec_receive_frame_kt);
    return HRTIMER_RESTART;
}

//hdmi cec interrupt quest handler
u32 hdmi_cec_gpio_irq_hdl(void *para)
{
	long unsigned long tmp_time;
	tmp_time = ktime_to_ns(ktime_get()) - last_irq_time;
	if(tmp_time >= 4200000 && tmp_time <= 4800000)
	{
		receive_frame_data.sta = header_block_receive_info_bit;
		hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_CONFIRM_LOGICAL_BIT_TIME);
		receive_frame_data.flags = 8;
		receive_frame_data.receive_frame = 0x0;
		receive_frame_data.oprands_count= 0;
		hrtimer_start(&hdmi_cec_receive_frame_hrtimer, hdmi_cec_receive_frame_kt, HRTIMER_MODE_REL);
	}
	else
	{
		hrtimer_start(&hdmi_cec_receive_frame_hrtimer, hdmi_cec_receive_frame_kt, HRTIMER_MODE_REL);
		last_irq_time = ktime_to_ns(ktime_get());
	}

    return 0;
}

//start receive frame
void hdmi_cec_start_receive_frame(void)
{
    hdmi_cec_receive_frame_kt = ktime_set(0, HDMI_CEC_JUDGE_START_BIT_TIME);
    receive_frame_data.sta = judge_start_bit;

    receive_frame_data.current_buf_flags= 0;
    receive_frame_data.add_buf_flags= 0;

    receive_frame_data.irq_request_handle = sw_gpio_irq_request(hdmi_cec_gpio, TRIG_EDGE_NEGATIVE,
                                                                (peint_handle)hdmi_cec_gpio_irq_hdl, (void *)hdmi_cec_gpio);
}

//get initiator address
__u32 hdmi_cec_get_initiator_adr(void)
{
    return (__u32)send_frame_data.indicate_logical_adr;
}

/* receive data from userspace */
void hdmi_cec_nl_input(struct sk_buff *skb)
{
    struct sk_buff *sk_b;
    struct nlmsghdr *nlh;
    char str[50];
    flag = 1;
    sk_b = skb_get(skb);
    if(sk_b->len >= NLMSG_SPACE(0)){
        nlh = nlmsg_hdr(sk_b);
		memcpy(str, NLMSG_DATA(nlh), sizeof(str));
        u_pid = nlh->nlmsg_pid;
     } 
     kfree_skb(sk_b);
}

//get receive message
__u32 hdmi_cec_receive_message(__hdmi_cec_receive_message *receive)
{
	//no message to receive
    if(receive_frame_data.current_buf_flags == receive_frame_data.add_buf_flags || receive_a_frame == 0)
        return 0;

	receive_a_frame = 0;
    receive_frame_data.current_buf_flags++;
    if(receive_frame_data.current_buf_flags > 9)
        receive_frame_data.current_buf_flags = 0;
	memcpy(receive, &receive_frame_buffer[receive_frame_data.current_buf_flags], sizeof(__hdmi_cec_receive_message));
	memset(&receive_frame_buffer[receive_frame_data.current_buf_flags], 0, sizeof(__hdmi_cec_receive_message));

    return 1;
}
static int hdmi_cec_open_sock(void)
{
	hdmi_cec_sock = netlink_kernel_create(&init_net, NETLINK_CEC, 0, hdmi_cec_nl_input, NULL, THIS_MODULE);
	if (!hdmi_cec_sock){
         __Cerr("##open sock failed.");
		 return -1;
		}
	return 0;
}
int hdmi_cec_close_flag(void)
{
    __hdmi_cec_receive_message res;
	res.oprands_num = 0;
	res.opcode = 0xee;
	memset(res.oprands, 0, 14 * sizeof(unsigned long));
	res.oprands[0] = 0;
	hdmi_cec_sent_sock_skb(&res);
	return 0;
}
static int hdmi_cec_close_sock(void)
{
    //send '0xee' msg to tell app to close the socket.
    hdmi_cec_close_flag();
	if (hdmi_cec_sock){
		sock_release(&hdmi_cec_sock->sk_socket);
		hdmi_cec_sock->sk_socket = NULL;
		}
	return 0;
}

int hdmi_cec_sent_sock_skb(__hdmi_cec_receive_message* rec_p)
{
	struct sk_buff* sk_data;
	struct msg_to_send msg_k;
    int msg_len, m_len;
	int ret;
	if (!rec_p && !hdmi_cec_sock){
          __Cerr("##arg or sock is null");
		  return -1;
	}
	if (!u_pid){
		__Cerr("##no message sent form user,the u_pid is unknown.\n");
		return 0;
	}
    /* total size of message */
	m_len = NLMSG_SPACE(sizeof (msg_k));
	sk_data = alloc_skb(m_len,GFP_ATOMIC);
	if(!sk_data){
          __Cerr("##alloc_skb error.");
		  return -1;
	}
	msg_k.nlh = nlmsg_put(sk_data,0,0,0,m_len,0);
	NETLINK_CB(sk_data).pid = 0;
    NETLINK_CB(sk_data).dst_group = 0;
	msg_len = sizeof(msg_k.sec);
	/* store rec_p in the sk_buff */
	memcpy(NLMSG_DATA(msg_k.nlh),rec_p,msg_len);
	
    ret = netlink_unicast(hdmi_cec_sock, sk_data, u_pid, MSG_DONTWAIT);
	return 0;
}

static void send_cec_data_to_app(__hdmi_cec_receive_message *send_data)
{
#if 0
    char buf1[64];
	char buf2[64];
	char tmp[64];
	char *envp[3];
	int i;

	/*format: CECDATA=opcode;oprands_num */
	snprintf(buf1, sizeof(buf1), "CECDATA=%x;%d",send_data->opcode,send_data->oprands_num);
	envp[0] = buf1;
	/* oprands */
	if(send_data->oprands_num == 0){
		tmp[0] = '\0';
	}else {
	    for (i = 0;i < send_data->oprands_num && i < 64; i++){
           tmp[i] = (char)send_data->oprands[i];
	    printk("## get oprands[%d] = %x\n", i, send_data->oprands[i]);
		}
		tmp[++i] = '\0';
	}
	/*format: OPRAND=oprand */
    snprintf(buf2, sizeof(buf2), "OPRAND=%s",tmp); 
	envp[1] = buf2;
	envp[2] = NULL;

	kobject_uevent_env(&ghdmi.dev->kobj, KOBJ_CHANGE, envp);
	__Cinf("##send_data->opcode =%x\n",send_data->opcode);
	#endif
}

int hdmi_cec_run_thread(void *parg)
{
	while(1)
	{
		__hdmi_cec_receive_message cec_receive_data;
        if(kthread_should_stop())
        {
            __inf("hdmi_cec_run_thread, hdmi suspend\n");
            break;
        }
		if(hdmi_cec_receive_message(&cec_receive_data) == 1)
		{
			int i;
			send_cec_data_to_app(&cec_receive_data);
			__disp_cec_data_process(&cec_receive_data);
			//printk("##send opcode =0x%x oprands_num = %d\n",cec_receive_data.opcode, cec_receive_data.oprands_num);
			//for (i = 0;i < cec_receive_data.oprands_num && i < 64; i++)
			//	printk("oprands[%d] = %x\n", i, cec_receive_data.oprands[i]);
		}
	}

	return 0;
}

static int hdmi_cec_input_dev(void)
{
	int i;
	int ret;
	hdmicecdev = input_allocate_device();
	if (!hdmicecdev) {
		printk("alloc hdmi cec input dev failed\n");
		return -ENODEV;
	}

	hdmicecdev->name = "hdmi-cec";
	hdmicecdev->phys = "";
	hdmicecdev->id.bustype = BUS_HOST;
	hdmicecdev->id.vendor = 0x0001;
	hdmicecdev->id.product = 0x0001;
	hdmicecdev->id.version = 0x0100;
	hdmicecdev->open = NULL;
	hdmicecdev->close = NULL;
	set_bit(EV_KEY, hdmicecdev->evbit);
	//set_bit(KEY_POWER, hdmicecdev->keybit);
	for(i=0; i < sizeof(cec_key_tab)/sizeof(__disp_cec_key_tab); i++)
		set_bit(cec_key_tab[i].input_key, hdmicecdev->keybit);
	
	ret = input_register_device(hdmicecdev);
	if(ret) {
		printk("Unable to Register the hdmi cec key\n");
	}

	return ret;
}

void hdmi_cec_init(void)
{
    //hdmi cec
    struct regulator *regu;
    int ret;
    int pin = -1;
	int pin_num = -1;
    //wifi
    struct regulator *regu_wifi;
    int ret_wifi;

    //hdmi cec
    regu = regulator_get(NULL,"axp22_dldo3");
    if(IS_ERR(regu)) {
        printk("some error happen, fail to get regulator!");
        return;
        }

    //set output voltage to 3V
    ret = regulator_set_voltage(regu, 3300000, 3300000);
    if(IS_ERR(regu)) {
        printk("some error happen, fail to set regulator voltage!");
        return;
        }

    //enable regulator
    ret = regulator_enable(regu);
    if(IS_ERR(regu)) {
        printk("some error happen, fail to enable regulator !");
        return;
        }
    //put regulator when module exit
    regulator_put(regu);

    //wifi
    regu_wifi = regulator_get(NULL,"axp22_aldo1");
    if(IS_ERR(regu_wifi)) {
        printk("some error happen, fail to get regulator!");
        return;
        }

    //set output voltage to 3V
    ret = regulator_set_voltage(regu_wifi, 3300000, 3300000);
    if(IS_ERR(regu_wifi)) {
        printk("some error happen, fail to set regulator voltage!");
        return;
        }

    //enable regulator
    ret_wifi = regulator_enable(regu_wifi);
    if(IS_ERR(regu_wifi)) {
        printk("some error happen, fail to enable regulator !");
        return;
        }
    //put regulator when module exit
    regulator_put(regu_wifi);
    //gpio init
    //hdmi_cec_gpio = GPIOG(17);//base on hardware.
    pin = hdmi_cec_get_pin();
	pin_num = hdmi_cec_get_pin_num();
	if(pin == -1 || pin == -1)
		return;

    hdmi_cec_gpio = hdmi_cec_set_gpio(pin,pin_num);
	if(hdmi_cec_gpio < 0){
		hdmi_cec_used = 0;
		ghdmi.hdmi_cec_cfg.hdmi_cec_used = 0;
		ghdmi.hdmi_cec_cfg.hdmi_cec_pin = -1;
		ghdmi.hdmi_cec_cfg.hdmi_cec_pin_num = -1;
		return;
		}
    irq_num = gpio_to_irq(hdmi_cec_gpio);
    sw_gpio_setcfg(hdmi_cec_gpio, 1);

    //init polling message
    polling_mes_data.broadcast_complete_flags = 0;
    hrtimer_init(&hdmi_cec_polling_message_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    hdmi_cec_polling_message_hrtimer.function = hdmi_cec_polling_hrtimer_handler;

    //init send frame variable
    hrtimer_init(&hdmi_cec_send_frame_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    hdmi_cec_send_frame_hrtimer.function = hdmi_cec_send_frame_hrtimer_handler;

    send_frame_data.indicate_logical_adr = HDMI_CEC_LADDR_PLAYER1;
    send_frame_data.follower_logical_adr = HDMI_CEC_LADDR_TV;

    if(send_frame_data.follower_logical_adr == HDMI_CEC_LADDR_BROADCAST)
        send_frame_data.ack = HDMI_CEC_MSG_ACK_BROADCAST;
    else
        send_frame_data.ack = HDMI_CEC_MSG_ACK_NORMAL;

    send_frame_data.add_buf_flags = 0;
    send_frame_data.current_buf_flags = 0;

    /*init receive frame variable */
    hrtimer_init(&hdmi_cec_receive_frame_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    hdmi_cec_receive_frame_hrtimer.function = hdmi_cec_receive_frame_hrtimer_handler;
	CEC_task = kthread_create(hdmi_cec_run_thread, (void*)0, "cec proc");
	if(IS_ERR(CEC_task))
	{
		ret = PTR_ERR(CEC_task);
		CEC_task = NULL;
		printk("Unable to start kernel thread %s. ret = %x\n","cec proc", ret);
		return;
	}
	wake_up_process(CEC_task);
    //hdmi_cec_open_sock();     /* if use netlink to send data,open it */
    hdmi_cec_start_receive_frame();
	hdmi_cec_input_dev();
}

void hdmi_cec_exit(void)
{
    //hdmi cec
    struct regulator *regu;
    int ret;

    //wifi
    struct regulator *regu_wifi;
    int ret_wifi;

    //hdmi cec
    regu = regulator_get(NULL,"axp22_dldo3");
    if(IS_ERR(regu)) {
        printk("some error happen, fail to get regulator!");
        return;
        }

    //diable regulator
    ret = regulator_disable(regu);
    if(IS_ERR(regu )) {
        printk("some error happen, fail to disable regulator !");
        return;
        }

    //put regulator when module exit
    regulator_put(regu);

    //wifi
    regu_wifi = regulator_get(NULL,"axp22_aldo1");
    if(IS_ERR(regu_wifi)) {
        printk("some error happen, fail to get regulator!");
        return;
        }

    //diable regulator
    ret_wifi = regulator_disable(regu_wifi);
    if(IS_ERR(regu_wifi )) {
        printk("some error happen, fail to disable regulator !");
        return;
        }

    //put regulator when module exit
    regulator_put(regu_wifi);

    //cancel high resolution timer
    hrtimer_cancel(&hdmi_cec_send_frame_hrtimer);
    hrtimer_cancel(&hdmi_cec_receive_frame_hrtimer);
    //free gpio
    sw_gpio_irq_free(receive_frame_data.irq_request_handle);
	if(CEC_task)
	{
		kthread_stop(CEC_task);
		CEC_task = 0;
	}
	//hdmi_cec_close_sock();
	input_unregister_device(hdmicecdev);
}
